Match single quotes in one O(n) pass instead of an O(n^2) scan#249
Merged
Conversation
buildSingleQuoteMatchCache() collected potential openers and closers into two lists, then matched each closer against the opener list with a nested scan - O(closers x openers), which degraded badly on quote-heavy text. Fold the classification and matching into a single forward pass: push each opener onto a stack and, at a closer, pop the top opener. The stack top is always the largest-index still-open opener, so popping it reproduces the former nearest-preceding-unmatched-opener pairing exactly, now in O(n). Behavior is unchanged - the smart-quote semantics (including the official conformance corpus) are identical; only the cost changes. Also drops the unused findMatchingSingleQuoteCloser() relic of the old approach.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #249 +/- ##
============================================
+ Coverage 92.10% 92.37% +0.27%
+ Complexity 3596 3576 -20
============================================
Files 107 107
Lines 10167 10129 -38
============================================
- Hits 9364 9357 -7
+ Misses 803 772 -31 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
InlineParser::buildSingleQuoteMatchCache()decides which single quotes are curly opener/closer pairs and which are apostrophes. It collected potential openers and closers into two lists, then matched each closer against the opener list with a nested scan - O(closers x openers). On quote-heavy text that is quadratic and noticeably slow.This folds classification and matching into a single forward pass using an opener stack:
The stack top is always the largest-index still-open opener, so popping it reproduces the former "nearest preceding unmatched opener" pairing exactly - now in O(n).
Behavior
Unchanged. The smart-quote semantics, including the official conformance corpus (
tests/official/smart.test), render identically; only the cost changes. A newSingleQuoteMatchingTestlocks the pairing for matched, lone-opener, nested, and apostrophe-then-pair cases.This is a perf-only, behavior-preserving change. It deliberately does NOT adopt carve-php's flanking-rule rewrite (which drops opener/closer matching entirely); that diverges from djot.js semantics and would break the conformance corpus. Only the O(n) stack-merge idea is carried over.
Also removes the unused
findMatchingSingleQuoteCloser()method, a leftover of the old approach.